package view;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

import java.util.*;

import java.awt.Color;
import java.awt.Point;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import net.TockClient;
import net.UpdatePacket;

import model.*;


/**
* This code was edited or generated using CloudGarden's Jigloo
* SWT/Swing GUI Builder, which is free for non-commercial
* use. If Jigloo is being used commercially (ie, by a corporation,
* company or business for any purpose whatever) then you
* should purchase a license for each developer using Jigloo.
* Please visit www.cloudgarden.com for details.
* Use of Jigloo implies acceptance of these licensing terms.
* A COMMERCIAL LICENSE HAS NOT BEEN PURCHASED FOR
* THIS MACHINE, SO JIGLOO OR THIS CODE CANNOT BE USED
* LEGALLY FOR ANY CORPORATE OR COMMERCIAL PURPOSE.
*/
public class GameGui extends JFrame implements TockConstants {
	//----- What the hell is this for?
	public static final long serialVersionUID = 0;
	
	private JLabel boardImage;
	
	private JLabel[][] marbles;
	private JLabel[][] altMarbles;
	
	private JLabel[] cards;
	
	private JLabel[] targets;
	
	private Location[][] reverse_lookup;
	
	private JTextArea chatArea;
	private JTextField chatEntry;
	private JButton submitChat;
	private JButton chatClearButton;
	private JButton register;
	private JButton exit;
	private JScrollPane scrollPane;
	private JTextArea users;
	private JLabel lastCard;
	private JLabel cardDeck;
	private JLabel turnIndLabel;
	private JLabel playerLabel;
	
	private boolean registered;
	private boolean playing;
	private boolean pieceSelected;
	private boolean cardSelected;
	private boolean targetSelected;
	private boolean altMarbleSelected;
	private JLabel selectedPiece;
	private JLabel selectedTarget;
	private JLabel selectedAltMarble;
	private int selectedCard;
	private ArrayList<String> cardIDs;
	
	private boolean				doingSeven; // If we're dealing with a 7 split
	private int 				sevenCount; // The remaining spots we can move with a 7 split
	private ArrayList<Location> sevenLocations; // The locations {src, dest, src, dest, etc} of the 7 split
	private int[][]				sevenMarbleFreq; // Keeps track of what marble a corresponding altMarble is for
	private Location[]			saveBoard; // Keeps the 4 board locations during a 7 split
	
	private TockClient 	client;
	private TockAPP		theAPP;
	
	private UpdatePacket updater;
	private String sender;
	private int gameID;
	
	public boolean MY_TURN = false;
	
	private TockBoard board;
	private TockChat chat;
	private ArrayList<String> hand;
	
	public void setUpdater(UpdatePacket pack) {updater = pack;}
	
	/**
	 * @return the client
	 */
	public TockClient getClient() {
		return client;
	}

	public GameGui(String host, int gameID) {this(new TockClient(host), null, gameID);}
	
	public GameGui(TockClient client, TockAPP theAPP, int gameID) {
		//----- Yawn
		super("UNREGISTERED Client");
		this.client = client;
		this.gameID = gameID;
		this.theAPP = theAPP;
		
		//----- Create all the special gui components
		marbles = new JLabel[4][4];
		altMarbles = new JLabel[4][7];
		targets = new JLabel[16];
		
		//----- Reverse lookup table for mapping point->Location object
		reverse_lookup = new Location[600][600];
		
		//----- Updater and card id's		
		updater = new UpdatePacket(false);
		cardIDs = TockDeck.createDeck();
		
		//----- We start with nothing selected
		pieceSelected = false;
		altMarbleSelected = false;
		
		//----- Create all the nastiness needed for 7-split
		sevenMarbleFreq = new int[4][7];
		for (int i=0; i<sevenMarbleFreq.length; i++)
			for (int j=0; j<sevenMarbleFreq[0].length; j++)
				sevenMarbleFreq[i][j] = -1;
		saveBoard = new Location[4];
		
		//----- Our version of the board, chat, and hand
		board = new TockBoard();
		chat = new TockChat();
		hand = new ArrayList<String>();
		
		calcReverse();	// Maping
		initGUI();		// Create all the gui components
		addListeners();	// Add the listeners
		update();		// Update the board
		
		//----- Bad size...needs to be fixed
		this.setSize(700, 600);
		getContentPane().setBackground(new java.awt.Color(0,0,0));
		setVisible(true);
		
		//----- Grab the person's name and register them
		sender = JOptionPane.showInputDialog("Please enter your name");
		registered = true;
		playing = false;
		client.sendRegisterPacket(sender, gameID);
		setTitle("Tock " + VERSION + "(" + sender + ")");
		
		//----- After registering we run the thread to update the client
		UpdaterThread updaterThrd = new UpdaterThread(this);
		updaterThrd.start();
	}
	
	private void initGUI() {
		try {
			Container pane = this.getContentPane();
			setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
			pane.setLayout(null);
			
			{
				turnIndLabel = new JLabel();
				pane.add(turnIndLabel);
				turnIndLabel.setIcon(new ImageIcon(getClass().getClassLoader().getResource(TURN_INDICATORS[0])));
				turnIndLabel.setLocation(TURN_INDICATOR_LOCS[0][0], TURN_INDICATOR_LOCS[0][1]);
				turnIndLabel.setSize(48,48);
				turnIndLabel.setVisible(true);
			}
			{
				playerLabel = new JLabel();
				pane.add(playerLabel);
				playerLabel.setIcon(new ImageIcon(getClass().getClassLoader().getResource(SMILE_ICON)));
				playerLabel.setLocation(0,0);
				playerLabel.setSize(32,32);
				playerLabel.setVisible(false);
			}
			{
				for (int i=0; i<targets.length; i++) { // 16 just to be sure we cover everything
					targets[i] = new JLabel();
					pane.add(targets[i]);
					targets[i].setIcon(new ImageIcon(getClass().getClassLoader().getResource(TARGET_IMAGE)));
					targets[i].setLocation(0,0);
					targets[i].setSize(13, 13);
					targets[i].setVisible(false);
				}
			}
			{
				for (int i=0; i<4; i++) { // The 4 colours
					for (int j=0; j<7; j++) { // The possible 7 temp marble spots
						altMarbles[i][j] = new JLabel();
						pane.add(altMarbles[i][j]);
						altMarbles[i][j].setIcon(new ImageIcon(getClass().getClassLoader()
								.getResource(MARBLE_IMAGE_ALT[i])));
						altMarbles[i][j].setLocation(0,0);
						altMarbles[i][j].setSize(13,13);
						altMarbles[i][j].setVisible(false);
					}
				}
			}
			{
				for (int i=0; i<4; i++) { // The 4 colours
					for (int j=0; j<4; j++) { // The marble number
						marbles[i][j] = new JLabel();
						pane.add(marbles[i][j]);
						marbles[i][j].setIcon(new ImageIcon(getClass().getClassLoader()
								.getResource(MARBLE_IMAGE[i])));
						marbles[i][j].setLocation(START_SPOTS[i][j]);
						marbles[i][j].setSize(13, 13);
					}
				}
			}
			{
				cards = new JLabel[5];
				for (int i=4; i>=0; i--) {
					cards[i] = new JLabel();
					pane.add(cards[i]);
					cards[i].setLocation(new Point(580, (20 + (30*i))));
					cards[i].setSize(71, 96);
				}
				
				lastCard = new JLabel();
				pane.add(lastCard);
				lastCard.setLocation(new Point(440, 300));
				lastCard.setSize(71, 96);
				
				cardDeck = new JLabel();
				pane.add(cardDeck);
				cardDeck.setLocation(new Point(540, 300));
				cardDeck.setSize(71,96);
				cardDeck.setVisible(true);
				cardDeck.setIcon(new ImageIcon(CARD_BACKS[0]));
			}
			{
				boardImage = new JLabel();
				pane.add(boardImage);
				boardImage.setIcon(new ImageIcon(getClass().getClassLoader().getResource("media/TockBoard.JPG")));
				boardImage.setBounds(0, 0, 553, 423);
			}
			{
				chatArea = new JTextArea();
				chatArea.setLineWrap(true);
				chatArea.setEnabled(false);
				chatArea.setDisabledTextColor(Color.BLACK);
				chatArea.setEditable(false);
				scrollPane = new JScrollPane(chatArea);
				scrollPane.setLocation(15, 425);
				scrollPane.setSize(340, 70);
				pane.add(scrollPane);
						
				chatEntry = new JTextField(20);
				chatEntry.setLocation(15, 600);
				pane.add(chatEntry);
				chatEntry.setBounds(14, 511, 336, 21);

				submitChat = new JButton();
				submitChat.setIcon(new ImageIcon(SEND_ICON));
				submitChat.setLocation(470, 600);
				pane.add(submitChat);
				submitChat.setBounds(371, 504, 35, 35);
			}
			{
				register = new JButton();
				register.setIcon(new ImageIcon(START_ICON));
				register.setLocation(745, 40);
				pane.add(register);
				register.setBounds(483, 427, 33, 33);

				exit = new JButton();
				exit.setIcon(new ImageIcon(EXIT_ICON));
				exit.setLocation(745, 100);
				pane.add(exit);
				exit.setBounds(483, 476, 33, 33);
			}
			{
				users = new JTextArea();
				users.setLineWrap(true);
				users.setEnabled(false);
				users.setDisabledTextColor(Color.BLACK);
				users.setLocation(575, 425);
				pane.add(users);
				users.setBounds(371, 427, 91, 63);
			}
			{
				chatClearButton = new JButton();
				pane.add(chatClearButton);
				chatClearButton.setIcon(new ImageIcon(CLEAR_ICON));
				chatClearButton.setBounds(420, 504, 35, 35);
			}

			pack();
			//this.setSize(553, 434);
			//this.setSize(900, 700);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	
	
	private void addListeners() {
		addMarbleListeners();
		addCardListeners();
		addTargetListeners();
		
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent event) {
				goOffline();
			}
		});
		
		
		register.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent theEvent) {
				if (!playing) {
					client.sendSitPacket(sender, gameID);
					register.setIcon(new ImageIcon(STOP_ICON));
					playing = true;
				} else {
					client.sendStandPacket(sender, gameID);
					register.setIcon(new ImageIcon(START_ICON));
					playing = false;
					
					playerLabel.setLocation(0,0);
					playerLabel.setVisible(false);
				}
			}
		});
		
		exit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent theEvent) {
				goOffline();
			}
		});
				
		chatEntry.addKeyListener(new KeyListener() {
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					String text = chatEntry.getText();
					chatEntry.setText("");
					if ((!text.equals("")) && (text != null)) {
						client.sendChatPacket(sender, text, gameID);							
					}
				}
			}
			public void keyReleased(KeyEvent e) {}
			public void keyTyped(KeyEvent e) {}
		});
		
		submitChat.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent theEvent) {
				String text = chatEntry.getText();
				chatEntry.setText("");
				if ((!text.equals("")) && (text != null)) {
					client.sendChatPacket(sender, text, gameID);
				}
			}
		});
		
		chatClearButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent theEvent) {
				chat.clear();
				
				chatArea.setText("");
			}
		});
	}
	
	private void addTargetListeners() {
		for (int i=0; i<targets.length; i++) {
			final int index = i;
			targets[i].addMouseListener(new MouseListener() {
				public void mouseClicked(MouseEvent theEvent) {
					if (MY_TURN) {
						if ((pieceSelected || altMarbleSelected) && cardSelected) {
							targetSelected = true;
							selectedTarget = targets[index];
							
							handleMove();
						}
						
						if (DEBUG_LISTENER) System.out.println("LISTENER (target): " + index);
					}
				}
				public void mouseEntered(MouseEvent e) {}
				public void mouseExited(MouseEvent e) {}
				public void mousePressed(MouseEvent e) {}
				public void mouseReleased(MouseEvent e) {}
			});
		}
	}
	
	private void addMarbleListeners() {
		//----- Normal marbles
		for (int col = 0; col < 4; col++) {
			for (int num = 0; num < 4; num++) {
				final int colour = col;
				final int number = num;
				getMarble(col, num).addMouseListener(new MouseListener() {
					public void mouseClicked(MouseEvent theEvent) {
						if (MY_TURN) {
							if (pieceSelected) {
								selectedPiece.setEnabled(true);
							}
							if (selectedPiece == getMarble(colour, number)) {
								pieceSelected = false;
								selectedPiece = null;
							} else {
								pieceSelected = true;
								selectedPiece = getMarble(colour, number);
								selectedPiece.setEnabled(false);
								
								if (altMarbleSelected) {
									selectedAltMarble.setEnabled(true);
									altMarbleSelected = false;
									selectedAltMarble = null;
								}
							}
							
							resetTargets();
							handleMove();
							
							if (DEBUG_LISTENER) System.out.println("LISTENER (marble): " + colour + " / " + number);
						}
					}
					public void mouseEntered(MouseEvent e) {}
					public void mouseExited(MouseEvent e) {}
					public void mousePressed(MouseEvent e) {}
					public void mouseReleased(MouseEvent e) {}
				});
			}
		}
		
		//----- Alt marbles for the 7-splits
		for (int col = 0; col < 4; col++) {
			for (int num = 0; num < 7; num++) {
				final int colour = col;
				final int number = num;
				getAltMarble(col, num).addMouseListener(new MouseListener() {
					public void mouseClicked(MouseEvent theEvent) {
						if ((MY_TURN) && (colour == BOARD_LOCATIONS[updater.getUsers().numOfPlayers()][updater.getUsers().getPlayerIndex(sender)])) {
							if (altMarbleSelected) {
								selectedAltMarble.setEnabled(true);
							}
							if (selectedAltMarble == getAltMarble(colour, number)) {
								altMarbleSelected = false;
								selectedAltMarble = null;
							} else {
								altMarbleSelected = true;
								selectedAltMarble = getAltMarble(colour, number);
								selectedAltMarble.setEnabled(false);
								
								if (pieceSelected) {
									selectedPiece.setEnabled(true);
									pieceSelected = false;
									selectedPiece = null;
								}
							}
							
							resetTargets();
							handleMove();
							
							if (DEBUG_LISTENER) System.out.println("LISTENER (marble): " + colour + " / " + number);
						}
					}
					public void mouseEntered(MouseEvent e) {}
					public void mouseExited(MouseEvent e) {}
					public void mousePressed(MouseEvent e) {}
					public void mouseReleased(MouseEvent e) {}
				});
			}
		}
	}
	private void addCardListeners() {
		for (int i=0; i<cards.length; i++) {
			final int index = i;
			cards[i].addMouseListener(new MouseListener() {
				public void mouseClicked(MouseEvent theEvent) {
					if (MY_TURN) {
						if (cardSelected) {
							cards[selectedCard].setEnabled(true);
						}
						if (selectedCard == index) {
							cardSelected = false;
							selectedCard = -1;
						} else {
							cardSelected = true;
							selectedCard = index;
							cards[selectedCard].setEnabled(false);
						}
						
						clearSeven();
						
						if (selectedCard != -1) {
							if (TockDeck.valueOfCard(Integer.parseInt(hand.get(selectedCard))) == 7) {
								doingSeven = true;
								sevenCount = 7;
								sevenLocations = new ArrayList<Location>();
								
								int col = BOARD_LOCATIONS[updater.getUsers().numOfPlayers()][updater.getUsers().getPlayerIndex(sender)];
								saveBoard[0] = board.getLocation(col, 0);
								saveBoard[1] = board.getLocation(col, 1);
								saveBoard[2] = board.getLocation(col, 2);
								saveBoard[3] = board.getLocation(col, 3);
							}
						}
						
						resetTargets();
						handleMove();
						
						if (DEBUG_LISTENER) System.out.println("LISTENER (card): " + index + " - " + hand.get(index));
					}
				}
				public void mouseEntered(MouseEvent e) {}
				public void mouseExited(MouseEvent e) {}
				public void mousePressed(MouseEvent e) {}
				public void mouseReleased(MouseEvent e) {}
			});
		}
	}
	
	private void handleMove() {
		//----- Instead of integrating it everywhere, we'll keep the 7-split seperate
		if (doingSeven) {
			//--- Get info on the sender, card index
			String id = (String)hand.get(selectedCard);
			int cardIndex = Integer.parseInt(id);
			
			if (pieceSelected || altMarbleSelected) {
				//--- Get info on the source/destination locations and colour
				Location source = (altMarbleSelected) ? lookupLocation(selectedAltMarble.getLocation()) : 
					lookupLocation(selectedPiece.getLocation());
				
				int col = BOARD_LOCATIONS[updater.getUsers().numOfPlayers()][updater.getUsers().getPlayerIndex(sender)];
				
				//--- We've selected our next 7-split move
				if (targetSelected) {
					Point temp = selectedTarget.getLocation();
					Location dest = lookupLocation(temp);
					
					int moveDiff = TockBoard.calcDistance(source, dest);
					int memory = 0;
					
					//- Unselect the piece
					if (altMarbleSelected) {
						int[] indicies = getAltMarbleIndicies(selectedAltMarble);
						memory = sevenMarbleFreq[indicies[0]][indicies[1]]; 
						
						altMarbleSelected = false;
						selectedAltMarble.setEnabled(true);
						selectedAltMarble.setLocation(0,0);
						selectedAltMarble.setVisible(false);
						selectedAltMarble = null;
					} else {
						memory = getMarbleIndicies(selectedPiece)[1];
						
						pieceSelected = false;
						selectedPiece.setEnabled(false); // We leave it disabled so we don't select that piece again
						selectedPiece = null;
					}
					
					//- Put the alt marble in
					int altNum = (int)(sevenLocations.size()/2);
					altMarbles[col][altNum].setLocation(TockBoard.findLocation(dest));
					altMarbles[col][altNum].setVisible(true);
					sevenMarbleFreq[col][altNum] = memory;
					
					//- Update the board
					board.setLocation(col, memory, dest);
					
					//- Record the move
					sevenLocations.add(source);
					sevenLocations.add(dest);
					
					//- Reduce the amount left on 7 split
					sevenCount -= moveDiff;
					
					resetTargets();
					
					//- Check if the seven split is all done
					if (sevenCount == 0) sendSeven(cardIndex);
				} else { //- Show the targets...
					//-- Get the targets
					ArrayList<Location> moves = TockGame.getPossibleMoves(source, 7,
							updater.getUsers().getPlayerIndex(sender), 
							updater.getUsers().numOfPlayers(), board, sevenCount);
					
					if (DEBUG_MOVE_GENERATION) {
						System.out.println("Found " + moves.size() + " locations:");
						for (int i=0; i<moves.size(); i++) {
							System.out.println(moves.get(i));
						}
					}
					
					//--  Display them
					for (int i=0; i<moves.size(); i++) {
						targets[i].setLocation(TockBoard.findLocation(moves.get(i)));
						targets[i].setVisible(true);
					}
				}
			} else { //--- We check to see if we should discard the 7.
				if (!canMove()) {
					int result = JOptionPane.showConfirmDialog(this, "Do you want to discard?", "Discard Card?", JOptionPane.OK_OPTION);
					if (0 == result) {
						client.sendMovePacket(sender, cardIndex, null, null, null, MOVE_PACKET_TYPE_DISCARD, gameID);
					}
					cardSelected = false;
					cards[selectedCard].setEnabled(true);
					selectedCard = -1;
					clearSeven();
				}
			}
		}
		
		//----- If we've got our piece and our card selected
		else if (pieceSelected && cardSelected) {
			//--- Get info on the sender, card index, source/destination locations
			String id = (String)hand.get(selectedCard);
			int cardIndex = Integer.parseInt(id);
			Point temp = selectedPiece.getLocation();
			Location source = lookupLocation(temp);
						
			//--- If we've got our target, make the move
			if (targetSelected) {
				temp = selectedTarget.getLocation();
				Location dest = lookupLocation(temp);
				
				//--- Check if we want to tock or switch
				boolean tenTock = false;
				if ((source.type == LOC_TYPE_NORMAL) && (dest.type == LOC_TYPE_NORMAL) &&
						(10 == TockDeck.valueOfCard(cardIndex))) {
					
					int diff = dest.loc - source.loc;
					if (diff < 0) diff += 72;
					if (-1 != board.pieceAt(dest)[0]) {
						if (diff == 10) {
							int result = JOptionPane.showConfirmDialog(this, "Do you want to tock?", "Tock or Switch?", JOptionPane.OK_OPTION);
							if (0 == result) tenTock = true;
						} else tenTock = true;
					}
				}
				
				client.sendMovePacket(sender, cardIndex, source, dest, null, 
					(tenTock ? MOVE_PACKET_TYPE_TEN_SWITCH : MOVE_PACKET_TYPE_NORMAL), gameID);
					
				pieceSelected = false;
				selectedPiece.setEnabled(true);
				selectedPiece = null;
					
				cardSelected = false;
				cards[selectedCard].setEnabled(true);
				selectedCard = -1;
					
				resetTargets();
			}
			//--- If we don't, display the targets
			else {
				//-- Get the targets
				ArrayList<Location> moves = TockGame.getPossibleMoves(source, TockDeck.valueOfCard(cardIndex),
						updater.getUsers().getPlayerIndex(sender), 
						updater.getUsers().numOfPlayers(), board, sevenCount);
				
				if (DEBUG_MOVE_GENERATION) {
					System.out.println("Found " + moves.size() + " locations:");
					for (int i=0; i<moves.size(); i++) {
						System.out.println(moves.get(i));
					}
				}
				
				//--  Display them
				for (int i=0; i<moves.size(); i++) {
					targets[i].setLocation(TockBoard.findLocation(moves.get(i)));
					targets[i].setVisible(true);
				}
			}
		}
		//----- We want to see if they can discard
		else if (cardSelected) {
			//--- Get info on the sender, card index, source/destination locations
			String id = (String)hand.get(selectedCard);
			int cardIndex = Integer.parseInt(id);
			
			//--- Check if we've got a move
			if (!canMove()) {
				int result = JOptionPane.showConfirmDialog(this, "Do you want to discard?", "Discard Card?", JOptionPane.OK_OPTION);
				if (0 == result) {
					client.sendMovePacket(sender, cardIndex, null, null, null, MOVE_PACKET_TYPE_DISCARD, gameID);
				}
				cardSelected = false;
				cards[selectedCard].setEnabled(true);
				selectedCard = -1;
			}
		}
	}
	
	/**
	 * Helper function to determine if the player has a move or not.
	 * @return
	 */
	public boolean canMove() {
		//--- Get all the targets possible with all the cards and all the marbles
		ArrayList<Location> moves = new ArrayList<Location>();
		int sevenCount = 0;

		//-- For each of the cards
		for (int j=0; j<hand.size(); j++) {
			int tmpCount = 0;
			//-- For each of the marbles
			for (int i=0; i<4; i++) {
				Location source = board.getLocation(BOARD_LOCATIONS[updater.getUsers().numOfPlayers()][updater.getUsers().getPlayerIndex(sender)], i);

				if (7 == TockDeck.valueOfCard(Integer.parseInt(hand.get(j)))) {
					tmpCount += (TockGame.getPossibleMoves(source, TockDeck.valueOfCard(Integer.parseInt(hand.get(j))),
							updater.getUsers().getPlayerIndex(sender),
							updater.getUsers().numOfPlayers(), board, 7)).size();
				} else {
					moves.addAll(TockGame.getPossibleMoves(source, TockDeck.valueOfCard(Integer.parseInt(hand.get(j))),
							updater.getUsers().getPlayerIndex(sender),
							updater.getUsers().numOfPlayers(), board, 7));
				}
				sevenCount = Math.max(sevenCount, tmpCount);
			}
		}
		
		return ((moves.size() > 0) || (sevenCount >= 7));
	}
	
	public void update() {
		if (updater != null) {
			if (updater.getWorked()) {
				if (updater.getPlaying()) {
					//----- Indicate where we're sitting
					if (playerLabel.getX() == 0) {
						int pIndex = updater.getUsers().getPlayerIndex(sender);
						if (-1 != pIndex) {
							int spot = BOARD_LOCATIONS[updater.getUsers().numOfPlayers()][pIndex];
							playerLabel.setLocation(TURN_INDICATOR_LOCS[spot][0], TURN_INDICATOR_LOCS[spot][1]);
							playerLabel.setVisible(true);
						}
					}
					
					//----- Update whose turn it is
					int spot = BOARD_LOCATIONS[updater.getUsers().numOfPlayers()][updater.getUsers().getNextPlayer()];
					turnIndLabel.setLocation(TURN_INDICATOR_LOCS[spot][0], TURN_INDICATOR_LOCS[spot][1]);
					
					//----- Check if it's our turn
					MY_TURN = (updater.getUsers().getPlayer(updater.getUsers().getNextPlayer()).equals(sender));
					
					//----- Show how many cards are left
					//cardsLeft.setText(updater.getDeckRemaining() + "");
					
					//----- Update the hand
					hand = updater.getHand();
					updateHand();
					
					//----- Update the last card played
					if (updater.getLastCard().equals("")) {
						lastCard.setVisible(false);
					} else {
						lastCard.setVisible(true);
						lastCard.setIcon(new ImageIcon(CARD_IMAGE[cardIDs.indexOf(updater.getLastCard())]));
					}
					
					//----- Update the board
					if (!board.isEqual(updater.getBoard())) {
						board = updater.getBoard();
						updateBoard();
					}
				}
				
				//----- Update the chat window
				if ((updater.getMessage() != null) && (!(updater.getMessage().equals("")))) {
					chat.addMessage(updater.getMessage());
					
					String temp = "";
					ArrayList messages = chat.getMessages();
					for (int i = messages.size()-1; i >=0; i--) {
						temp += (String)messages.get(i);
						temp += "\n";
					}
					chatArea.setText(temp);
				}
							
				
				
				//----- Update the user/player list
				String temp2 = "";
				for (int i=0; i < updater.getUsers().numOfPlayers(); i++) {
					temp2 += (String)updater.getUsers().getPlayer(i) + " (P)";
					temp2 += "\n";
				}				
				for (int i=0; i<updater.getUsers().getChatters().size(); i++) {
					temp2 += (String)updater.getUsers().getChatters().get(i);
					temp2 += "\n";
				}				
				users.setText(temp2);
				
				updater.used();
			}
		}
	}
	
	/**
	 * Handles the sending of a 7-split. Does sanity check on if the marbles alternate, etc.
	 * @param card
	 */
	public void sendSeven(int card) {
		//----- Check if there isn't an overload of single-marble-moves
		boolean clearToSend = true;
		int count = 0;
		int[] freq = new int[4];
		
		for (int i=0; i<4; i++) {
			for (int j=0; j<7; j++) {
				if (sevenMarbleFreq[i][j] != -1) {
					count++;
					freq[sevenMarbleFreq[i][j]]++;
				}
			}
		}
		for (int i=0; i<4; i++)
			if (freq[i] > Math.ceil(((double)count)/2)) clearToSend = false;
		
		if (clearToSend) {
			//----- Send the info along
			client.sendMovePacket(sender, card, null, null, sevenLocations, MOVE_PACKET_TYPE_SEVEN_PARTIAL, gameID);
		} else {
			JOptionPane.showMessageDialog(this, "You need to alternate the 7-split...");
		}
		
		//----- Clear the stuff after dealing with the split
		cardSelected = false;
		cards[selectedCard].setEnabled(true);
		selectedCard = -1;
		clearSeven();
	}
	
	/**
	 * Clears and resets all of the needed variables after a 7-split craziness
	 *
	 */
	public void clearSeven() {
		for (int i=0; i<sevenMarbleFreq.length; i++)
			for (int j=0; j<sevenMarbleFreq[0].length; j++)
				sevenMarbleFreq[i][j] = -1;
		doingSeven = false;
		sevenCount = 7;
		sevenLocations = new ArrayList<Location>();
		resetAltMarbles();
		
		//----- Set back the board locations
		if (saveBoard[0] != null) {
			int col = BOARD_LOCATIONS[updater.getUsers().numOfPlayers()][updater.getUsers().getPlayerIndex(sender)];
			board.setLocation(col, 0, saveBoard[0]);
			board.setLocation(col, 1, saveBoard[1]);
			board.setLocation(col, 2, saveBoard[2]);
			board.setLocation(col, 3, saveBoard[3]);
			
			saveBoard[0] = null;
			saveBoard[1] = null;
			saveBoard[2] = null;
			saveBoard[3] = null;
		}
	}
	
	public void updateHand() {
		clearCards();
		for (int i=0; i<hand.size(); i++) {
			cards[i].setVisible(true);
			cards[i].setIcon(new ImageIcon(CARD_IMAGE[cardIDs.indexOf((String)hand.get(i))]));
		}
	}
	
	public void clearCards() {
		for (int i=0; i<cards.length; i++) {
			cards[i].setVisible(false);
		}
	}
	
	public void resetTargets() {
		targetSelected = false;
		selectedTarget = null;
		for (int i=0; i<targets.length; i++) {
			targets[i].setLocation(0, 0);
			targets[i].setVisible(false);
		}
	}
	
	public void resetAltMarbles() {
		altMarbleSelected = false;
		selectedAltMarble = null;
		for (int i=0; i<4; i++) {
			for (int j=0; j<7; j++) {
				altMarbles[i][j].setLocation(0,0);
				altMarbles[i][j].setVisible(false);
			}
		}
		
		//--- Re-enable the normal marbles
		for (int i=0; i<marbles.length; i++)
			for (int j=0; j<marbles[0].length; j++)
				marbles[i][j].setEnabled(true);
		if (selectedPiece != null)
			selectedPiece.setEnabled(false);
	}
	
	public void updateBoard() {
		for (int col=0; col<4; col++) {
			for (int num=0; num<4; num++) {
				getMarble(col, num).setLocation(TockBoard.findLocation(board.getLocation(col, num)));
			}
		}
	}
	
	
	public void goOffline() {
		if (registered) client.sendExitPacket(sender, gameID);
		if (null != theAPP) {
			theAPP.setVisible(true);
			theAPP.handleUpdate();
			theAPP = null;
		}
		this.dispose();
	}
	
	public JLabel getMarble(int colour, int marble_num) { return marbles[colour][marble_num]; }
	
	public JLabel getAltMarble(int colour, int marble_num) { return altMarbles[colour][marble_num]; }
	
	public int[] getAltMarbleIndicies(JLabel altMarb) {
		int[] toRet = new int[2];
		for (int i=0; i<4; i++) {
			for (int j=0; j<7; j++) {
				if (altMarbles[i][j].equals(altMarb)) {
					toRet[0] = i;
					toRet[1] = j;
					return toRet;
				}
			}
		}
		return toRet;
	}
	
	public int[] getMarbleIndicies(JLabel marb) {
		int[] toRet = new int[2];
		for (int i=0; i<4; i++) {
			for (int j=0; j<4; j++) {
				if (marbles[i][j].equals(marb)) {
					toRet[0] = i;
					toRet[1] = j;
					return toRet;
				}
			}
		}
		return toRet;
	}
	
	public void calcReverse() {
		//--- Do the normal spots
		for (int i=0; i<NORMAL_SPOTS.length; i++) {
			reverse_lookup[NORMAL_SPOTS[i].x][NORMAL_SPOTS[i].y] = new Location(LOC_TYPE_NORMAL, 0, i);
		}
		//--- Do the home spots
		for (int i=0; i<HOME_SPOTS.length; i++) {
			for (int j=0; j<HOME_SPOTS[0].length; j++) {
				reverse_lookup[HOME_SPOTS[i][j].x][HOME_SPOTS[i][j].y] = new Location(LOC_TYPE_HOME, i, j);
			}
		}
		//--- Do the start spots
		for (int i=0; i<START_SPOTS.length; i++) {
			for (int j=0; j<START_SPOTS[0].length; j++) {
				reverse_lookup[START_SPOTS[i][j].x][START_SPOTS[i][j].y] = new Location(LOC_TYPE_START, i, j);
			}
		}
	}
	
	public Location lookupLocation(Point p) {
		return reverse_lookup[p.x][p.y];
	}
	
	/**
	* Auto-generated main method to display this JFrame
	*/
	public static void main(String[] args) {
		//String host = JOptionPane.showInputDialog("Please enter host computer");
		//new GameGui(host);
	}
	
	
	
	// This is used to keep the client updated
	class UpdaterThread extends Thread implements TockConstants {
		
		public boolean RUNNING;
		private GameGui gui;
		
		/**
		 * Constructor...we hold on to the gui for future ref.
		 * @param gui
		 */
		public UpdaterThread(GameGui gui) {
			this.gui = gui;
			RUNNING = true;
		}
		
		public void run() {
			// We'll only update when we're running.
			while (RUNNING) {
				//--- We sleep for a bit (update every few seconds)
				try {Thread.sleep(UPDATE_FREQ);} catch (InterruptedException e){}
				
				//--- Get the update
				UpdatePacket packet = gui.getClient().sendUpdateRequest(gui.sender, gui.gameID);
				
				//--- Update the gui if it's not null
				if (null != packet) {
					gui.setUpdater(packet);
					gui.update();
				}
			}
			
		}
	}
	
	
}
